%* determine folder that program is in; %let program_path = %sysfunc(transtrn(%sysget(sas_execfilepath),%sysget(sas_execfilename),)); %include "&program_path.DS2SGTI - package source.sas"; %let image_viewer = C:\Users\Richard\FSViewer74\FSViewer.exe; %let image_folder = D:\TurtleOutput; options source; %if not %sysfunc(cexist(work.sasmacr.sganno.macro)) %then %do; %sganno %* create template for playing the annotations; ods path(prepend) work.templat(update); proc template; define statgraph DS2GTI; begingraph; layout overlay; annotate; %* specify the ANNOTATE GTL statement ; endlayout; endgraph; end; run; %end; %macro break; %abort cancel; %mend; %macro fdelete (filename); filename _file "&filename"; %let rc = %sysfunc(fdelete(_file)); %put NOTE: %sysfunc(sysmsg()); filename _file; %mend; options ls=250; ods html close; ods listing; proc ds2; * A data program; data _null_; * global variables; declare char(41) statements_table; /* table that will collect the generated statements */ declare varchar(400) write_options; /* options for %sgtext statements that will be generated */ declare varchar(300) path; /* path where image files will be written */ declare package canvas c; declare package turtle t; declare package math math(); method init(); * global assignments for use in other methods; statements_table = 'work.turtle_statements'; write_options = 'anchor="left", textsize=14, textfont="Albany AMT"'; /* * NOTE: * DS2 has no methods for interacting with the macro environment at run-time. * The closest one can get is to resolve a macro symbol at source code submission time */ path = %str(%'&image_folder%'); end; method sample1(); c = _new_ canvas(900,900, statements_table, path, 'sample1'); t = _new_ turtle(c); put statements_table=; t.down(); t.advance(200); t.arc(150,90,8); t.advance(200); t.arc(50,90,8); t.advance(450); t.arc(150,90,8); t.advance(450); t.arc(150,90,8); t.advance(450); t.arc(50,90,8); t.advance(200); t.arc(50,90,8); t.advance(300); t.arc(-50,180,8); t.right(90); t.advance(50); t.up(); t.setxy (-425,410); t.down(); t.write(c.filename(), write_options); c.close(); c.delete(); end; method sample2(); declare int index; c = _new_ canvas(900,900, statements_table, path, 'sample2'); t = _new_ turtle(c); t.down(); do index = 1 to 96; t.advance(10 + 10 * index); t.left(90); end; t.up(); t.setxy (-425,410); t.down(); t.write(c.filename(), write_options); t.delete(); c.close(); c.delete(); end; method sample3(); declare int index r g b; declare double theta; c = _new_ canvas(900,900, statements_table, path, 'sample3'); t = _new_ turtle(c); t.down(); r = 50; g = 100; b = 150; do index = 1 to 150; r = mod(r+2,256); g = mod(g+3,256); b = mod(b+4,256); t.color(c.rgb_to_color(r,g,b)); t.advance(25 + index); t.left(70); theta = constant('pi') + 8 * constant('pi') * index / 150; t.size(8 + 7 * cos(theta)); end; t.up(); t.setxy (-425,410); t.down(); t.write(c.filename(), write_options); t.delete(); c.close(); c.delete(); end; method spiros_by_ratio(int minp, int maxp); declare double radius angle chord_angle chord_length outside dstep; declare int p1 q1 p q; declare char(1) flag1 flag2; radius = 415; do p1 = minp to maxp; do q1 = 1 to maxp/2; math.rational ( 1.* p1 / q1, 1000, p, q ); flag1 = ifc (p < p1, '*', ' '); flag2 = ifc (p < 2*q,'#', ' '); * put p1=3. q1=3. p=3. q=3. ' ' flag1 ' ' flag2; if p < p1 then continue; if p < 2*q then continue; c = _new_ canvas(900,900, statements_table, 'spirals_by_ratio_anno', path, 'spiro ratio '||put(p,4.)||' '||put(q,4.)); t = _new_ turtle(c); angle = 360. / p; chord_angle = q * angle; chord_length = 2. * radius * sin(math.radians(chord_angle / 2)); outside = 360. * q / p; t.up(); t.setxy( radius * cos(math.radians(180 + (180-chord_angle)/2 )), radius * sin(math.radians(180 + (180-chord_angle)/2 )) ); t.down(); do dstep = 1 to p; t.advance(chord_length); t.left(outside); end; t.up(); t.setxy (-425,410); t.down(); t.write(c.filename(), write_options); t.up(); t.setxy (-425,375); t.down(); t.write(p||':'||q, write_options); t.up(); t.setxy (-425,340); t.down(); t.write(strip(put(angle,best7.)) || ' ' || strip(put(outside,best7.)), write_options); t.delete(); c.close(); c.delete(); end; end; end; /* spiros by ratio */ method spiros_by_angle(double mintheta, double maxtheta, double delta); declare double radius theta outside nangles angle chord_angle chord_length dstep; declare int p q steps; radius = 415; do theta = mintheta to maxtheta by delta; outside = 180. - theta; nangles = 360. / outside; math.rational ( nangles, 1000, p, q ); steps = p; angle = 360. / p; c = _new_ canvas(900,900, statements_table, 'spiros_by_angle_anno', path, 'spiro angle '||put(theta,6.2)); t = _new_ turtle(c); /* t.setxy(0,-radius); t.down(); t.arc(radius,360,180); t.up();*/ chord_angle = q * angle; chord_length = 2 * radius * sin(math.radians(chord_angle / 2)); * put theta=6.2 outside=6.2 p=4. q=4. nangles= angle= chord_length= chord_angle=; t.up(); t.setxy( radius * cos(math.radians(180 + (180-chord_angle)/2 )), radius * sin(math.radians(180 + (180-chord_angle)/2 )) ); t.down(); do dstep = 1 to steps; t.advance(chord_length); t.left(outside); end; t.up(); t.setxy (-425,410); t.down(); t.write(c.filename(), write_options); t.up(); t.setxy (-425,375); t.down(); t.write(p||' '||q, write_options); t.up(); t.setxy (-425,340); t.down(); t.write(angle, write_options); t.up(); t.setxy (-425,305); t.down(); t.write(theta, write_options); c.close(); c.delete(); end; end; /* spiros by angle */ method sexy_hexy(); declare int index edge; c = _new_ canvas(900,900, statements_table, path, 'sexy_bee'); t = _new_ turtle(c); t.right(120); t.advance(50); t.left(120); t.down(); do index = 1 to 6; t.advance(50); t.left(60); end; t.right(120); do edge = 1 to 6; do index = 1 to 5; t.advance(50); t.left(60); end; t.left(120); end; t.advance(50); t.left(60); t.advance(50); t.right(60); do edge = 1 to 12; do index = 1 to 5; t.advance(50); t.left(60); end; t.left(120); end; * make an arrow; t.left(135); t.advance(25); t.left(180); t.advance(25); t.right(90); t.advance(25); t.up(); t.setxy (-425,410); t.down(); t.write(c.filename(), write_options); t.delete(); c.close(); c.delete(); end; method swirly_triangle(); declare int index; c = _new_ canvas(900,900, statements_table, path, 'swirly_triangle'); t = _new_ turtle(c); t.down(); do index = 1 to 150; t.advance(8 + 6 * index); t.left(121); end; t.up(); t.setxy (-425,410); t.down(); t.write(c.filename(), write_options); t.delete(); c.close(); c.delete(); end; method run(); * rebuild statements table; sqlexec ('drop table ' || statements_table || ' force'); sqlexec ('create table ' || statements_table || ' (statement char(400))'); *sample1(); *sample2(); *sample3(); *spiros_by_ratio(5, 9); *spiros_by_angle(7, 10, 1); *sexy_hexy(); swirly_triangle(); end; enddata; run; quit; %let syslast = work.turtle_statements; options source xmin noxwait noxsync; options nosource2; filename stmt_pgm temp; data _null_; set &syslast; file stmt_pgm; put statement; run; %if 0=%sysfunc(fileref(stmt_pgm)) %then %do; %include stmt_pgm; %end; filename stmt_pgm; options noxwait noxmin noxsync; %sysexec "&image_viewer" "&image_folder.\swirly_triangle.png";